#include <iostream>

#include "tinyxml2.h"
#include "HTTPGet.hpp"
#include "YQLQuotes.hpp"



YQLQuotes::YQLQuotes()
{
  
}



void YQLQuotes::addSymbol(std::string symbol)
{
  this->symbolList.push_back(symbol);
}



void YQLQuotes::clearSymbols() 
{
  this->symbolList.clear();
}


// Builds the url from the symbol list
std::string YQLQuotes::buildURL() 
{
  // Base URL
  std::string url = "http://query.yahooapis.com/v1/public/yql?q="
		    "select%20%2a%20from%20yahoo.finance.quotes%20"
		    "where%20symbol%20in%20%28"; // %28 = '('
  
  // Symbol List
  for (unsigned int i = 0; i < this->symbolList.size(); ++i) {
    if (i != 0) { url += "%2C%20"; }               // comma and space
    url += "%27" + this->symbolList[i] + "%27";    // %27 = single quotes
  }            
  
  // Close symbol list
  url += "%29";       // %29 = ')'
  
  // Final
  url += "&diagnostics=true&env=store://datatables.org/alltableswithkeys";
  
  return url;
}


// Builds the URL, and then retreives the XML data.
bool YQLQuotes::loadFromYQL()
{
  HTTPGet httpGet;
  this->strURL  = this->buildURL();   
  this->loadSuccess = httpGet.getDocument(this->strURL, this->strXML);
  
  return (this->loadSuccess);   
}


// Walk the XML tree and locate each <QUOTE>. 
// Pass the individual quotes to processXMLQuote() to convert them into a ResultQuote
bool YQLQuotes::parseXML()
{
  // Initialize
  this->numQuoteErrors = 0;
  
  // Need valid data
  if (this->loadSuccess == false) { return false; }    
  
  // Create document handle, parse, and test for errors
  tinyxml2::XMLDocument doc;
  tinyxml2::XMLError parseError = doc.Parse( this->strXML.c_str() );
  if (parseError != tinyxml2::XML_NO_ERROR) { return false; }
  
  // Get a handle to the <QUERY><RESULTS> elements. <RESULTS> contains a list of <QUOTE> elements.
  // firstResult will be pointing at the very first <QUOTE>. 
  // firstResult->NextSiblingElement() is a pointer to the next <QUOTE> in the chain, NULL when no more elements
  tinyxml2::XMLHandle docHandle(&doc);
  tinyxml2::XMLElement* firstResult = docHandle.FirstChildElement("query").FirstChildElement("results").FirstChild().ToElement();
  if (!firstResult) { return false; }   
  
  // Step through the quotes, decoding each one
  for (tinyxml2::XMLElement* quote = firstResult; quote; quote = quote->NextSiblingElement()) 
  {
    if (!processXMLQuote(quote)) {
      this->numQuoteErrors++;
      continue;                    
    }
  }
  
  return true;
}


// Convert an individual XML <QUOTE> into a ResultQuote
bool YQLQuotes::processXMLQuote(tinyxml2::XMLElement* quote)
{
  // Null check
  if (quote == NULL) { return false; } 
  
  // Get the symbol name and null check
  if (quote->Attribute("symbol") == NULL) {
    // <QUOTE> must contain a symbol attribute
    // TODO yahoo.finance.quotes uses 'symbol' but yahoo.finance.historical uses 'Symbol'
    //      Is Attribute() case sensitive? 
    return false;  
  }
  
  // Convert the symbol name to a string
  std::string strSymbol = std::string(quote->Attribute("symbol"));
  if (strSymbol.empty()) {
    // Check for non-null but empty c-string by testing if string is empty
    return false; 
  }
  
  // Create ResultQuote, which will hold all our FieldValuePairs
  ResultQuote resultQuote(strSymbol);
  
  // Walk through the elements, pulling out each FieldValuePair
  for (tinyxml2::XMLElement* item = quote->FirstChild()->ToElement(); item; item = item->NextSiblingElement()) 
  {
    std::string strFieldName, strGetText;
    // The name is required, should never be null
    if (item->Name() == NULL) {
      return false;             
    } else {
      strFieldName = std::string(item->Name());  
    }
    // The text is option. If it is present, convert and use it. Otherwise keep strGetText empty.
    if (item->GetText() != NULL) {
      strGetText = std::string(item->GetText());  
    }
    
    // Lookup the fieldType, which identifies what type of Field this is
    FieldName fieldName = this->fieldNameLookup.StringToFieldName(strFieldName);
    FieldNameType fieldNameType = this->fieldNameLookup.FieldNameToFieldNameType(fieldName);
    
    // Add the pair to our results
    resultQuote.addRawPair(fieldName, strFieldName, strGetText, fieldNameType);
  }
  
  // Add to list of quotes
  this->vecQuotes.push_back(resultQuote);
  
  return true;
}


// Using the given index, fetch the specified ResultQuote from our internal vector.
bool YQLQuotes::getQuote(int index, ResultQuote& quote)
{
  if ((unsigned)index >= this->vecQuotes.size()) return false;
  quote = this->vecQuotes[index];
  return true;
}
